package net.xdclass.service.impl;

import ch.qos.logback.core.joran.util.beans.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.component.PayFactory;
import net.xdclass.config.RabbitMQConfig;
import net.xdclass.constant.CacheKey;
import net.xdclass.constant.TimeConstant;
import net.xdclass.enums.*;
import net.xdclass.exception.BizException;
import net.xdclass.feign.CouponFeignService;
import net.xdclass.feign.ProductFeignService;
import net.xdclass.feign.UserFeignService;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.model.LoginUser;
import net.xdclass.model.OrderMessage;
import net.xdclass.model.ProductOrderDO;
import net.xdclass.mapper.ProductOrderMapper;
import net.xdclass.model.ProductOrderItemDO;
import net.xdclass.request.*;
import net.xdclass.service.ProductOrderItemService;
import net.xdclass.service.ProductOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.xdclass.until.CommonUtil;
import net.xdclass.until.JsonData;
import net.xdclass.vo.*;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import springfox.documentation.spring.web.json.Json;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 二当家小D
 * @since 2022-02-22
 */
@Slf4j
@Service
public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, ProductOrderDO> implements ProductOrderService {

    @Autowired
    private UserFeignService userFeignService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private ProductOrderItemService productOrderItemService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private RabbitMQConfig rabbitMQConfig;
    @Autowired
    private PayFactory payFactory;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    @Transactional
    public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {
        LoginUser loginUser = LoginInterceptor.loginUserThreadLocal.get();
        String orderToken = orderRequest.getToken();
        if (StringUtils.isBlank(orderToken)) {
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_NOT_EXIST);
        }
        //原子操作 检验令牌 检出令牌
        String redisscript = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long result = stringRedisTemplate.execute(new DefaultRedisScript<Long>(redisscript, Long.class), Arrays.asList(
                String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY, loginUser.getId())), orderToken);
        if (result == 0L) {
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL);
        }

        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);
//获取收货地址
        ProductOrderAddressVO productOrderAddressVO = this.getUserAddress(orderRequest.getAddressId());
        log.info("收货地址信息:{}", productOrderAddressVO);
//获取用户加入购物车的商品
        List<Long> productIdList = orderRequest.getProductIdList();
        JsonData cartItemData = productFeignService.confirmOrderCartItem(productIdList);
        List<OrderItemVO> orderItemVOList = cartItemData.getData(new TypeReference<List<OrderItemVO>>() {
        });
        log.info("获取的商品:{}", orderItemVOList);
        if (orderItemVOList == null) {
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_CART_ITEM_NOT_EXIST);
        }
        //验证价格 减少商品优惠券
        this.checkPrice(orderItemVOList, orderRequest);
        //锁定优惠券
        this.lockCouponRecords(orderRequest, orderOutTradeNo);
        //锁定商品库存
        this.lockProductStocks(orderItemVOList, orderOutTradeNo);
        //创建订单
        ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest, loginUser, orderOutTradeNo, productOrderAddressVO);
//创建订单项
        this.saveProductOrderItems(orderOutTradeNo, productOrderDO.getId(), orderItemVOList);
        //发送延迟消息 自动关单
        OrderMessage orderMessage = new OrderMessage();
        orderMessage.setOutTradeNo(orderOutTradeNo);
        rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey(), orderMessage);
        //创建支付
        PayInfoVO payInfoVO = new PayInfoVO(productOrderDO.getOutTradeNo(), productOrderDO.getPayAmount(), orderRequest.getPayType(), orderRequest.getClientType()
                , orderOutTradeNo, "", TimeConstant.ORDER_PAY_TIMEOUT_MILLS);
        String payResult = payFactory.pay(payInfoVO);
        if (StringUtils.isBlank(payResult)) {
            return JsonData.buildSuccess(payResult);
        } else {
            log.error("创建支付订单失败:payinfoVO={},payResult={}", payInfoVO, payResult);
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
        }
    }

    /**
     * 创建订单项
     *
     * @param orderOutTradeNo
     * @param orderId
     * @param orderItemVOList
     */
    private void saveProductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemVOList) {
        List<ProductOrderItemDO> collect = orderItemVOList.stream().map(t -> {
            ProductOrderItemDO itemDO = new ProductOrderItemDO();
            itemDO.setBuyNum(t.getBuyNum());
            itemDO.setProductId(t.getProductId());
            itemDO.setProductImg(t.getProductImg());
            itemDO.setProductName(t.getProductTitle());
            //单价
            itemDO.setAmount(t.getAmount());
            //总价
            itemDO.setTotalAmount(t.getTotalAmount());
            itemDO.setProductOrderId(orderId);
            return itemDO;
        }).collect(Collectors.toList());
        boolean b = productOrderItemService.saveBatch(collect);
    }

    /**
     * 创建订单
     *
     * @param orderRequest
     * @param loginUser
     * @param orderOutTradeNo
     * @param productOrderAddressVO
     */
    private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String orderOutTradeNo, ProductOrderAddressVO productOrderAddressVO) {
        ProductOrderDO productOrderDO = new ProductOrderDO();
        productOrderDO.setUserId(loginUser.getId());
        productOrderDO.setHeadImg(loginUser.getHeadImg());
        productOrderDO.setNickname(loginUser.getName());

        productOrderDO.setOutTradeNo(orderOutTradeNo);
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setDel(0);
        productOrderDO.setOrderType(ProductOrderTypeEnum.DAILY.name());

        //实际字符价格
        productOrderDO.setPayAmount(orderRequest.getRealPayAmount());
        //总价 未使用优惠券价格
        productOrderDO.setTotalAmount(orderRequest.getTotalAmount());
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());

        productOrderDO.setReceiverAddress(JSON.toJSONString(productOrderAddressVO));

        baseMapper.insert(productOrderDO);
        return productOrderDO;
    }

    /**
     * 锁定商品库存
     *
     * @param orderItemVOList
     * @param orderOutTradeNo
     */
    private void lockProductStocks(List<OrderItemVO> orderItemVOList, String orderOutTradeNo) {
        List<OrderItemRequest> orderItemRequestList = orderItemVOList.stream().map(t -> {
            OrderItemRequest request = new OrderItemRequest();
            request.setBuyNum(t.getBuyNum());
            request.setProductId(t.getProductId());
            return request;
        }).collect(Collectors.toList());

        LockProductRequest lockProductRequest = new LockProductRequest();
        lockProductRequest.setOrderOutTradeNo(orderOutTradeNo);
        lockProductRequest.setOrderItemList(orderItemRequestList);

        JsonData jsonData = productFeignService.lockProductStock(lockProductRequest);
        if (jsonData.getCode() != 0) {
            log.error("锁定商品库存失败:{}", lockProductRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_LOCK_PRODUCT_FAIL);
        }
    }

    /**
     * 锁定优惠券
     *
     * @param orderRequest
     * @param orderOutTradeNo
     */

    private void lockCouponRecords(ConfirmOrderRequest orderRequest, String orderOutTradeNo) {
        ArrayList<Long> lockCouponRecordIds = new ArrayList<>();
        if (orderRequest.getCouponRecordId() > 0) {
            lockCouponRecordIds.add(orderRequest.getCouponRecordId());
            LockCouponRecordRequest lockCouponRecordRequest = new LockCouponRecordRequest();
            lockCouponRecordRequest.setOrderOutTradeNo(orderOutTradeNo);
            lockCouponRecordRequest.setLockCouponRecordIds(lockCouponRecordIds);
            //发起锁定优惠券请求
            JsonData jsonData = couponFeignService.lockCouponRecords(lockCouponRecordRequest);
            if (jsonData.getCode() != 0) {
                log.error("锁定优惠券失败:{}", lockCouponRecordRequest);
                throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
            }
        }
    }

    /**
     * 验证价格
     * 统计商品价格
     * 获取优惠卷 减去优惠券价格
     *
     * @param orderItemVOList
     * @param orderRequest
     */
    private void checkPrice(List<OrderItemVO> orderItemVOList, ConfirmOrderRequest orderRequest) {
        //统计商品总价格
        BigDecimal realPayAmount = BigDecimal.ZERO;
        if (orderItemVOList != null) {
            for (OrderItemVO orderItemVO : orderItemVOList) {
                BigDecimal totalAmount = orderItemVO.getTotalAmount();
                realPayAmount = realPayAmount.add(totalAmount);
            }
        }
        //获取优惠券 判断是否可以使用
        CouponRecordVO couponRecordVO = getCartCouponRecord(orderRequest.getCouponRecordId());
//计算购物车价格 是否满足 满减优惠券条件
        if (couponRecordVO != null) {
            if (realPayAmount.compareTo(couponRecordVO.getConditionPrice()) < 0) {
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
            }
            if (couponRecordVO.getPrice().compareTo(realPayAmount) > 0) {
                realPayAmount = BigDecimal.ZERO;
            } else {
                realPayAmount = realPayAmount.subtract(couponRecordVO.getPrice());
            }
        }
        if (realPayAmount.compareTo(orderRequest.getRealPayAmount()) != 0) {
            log.error("订单验价失败:{}", orderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }
    }

    private CouponRecordVO getCartCouponRecord(Long couponRecordId) {
        if (couponRecordId != null || couponRecordId < 0) {
            return null;
        }
        JsonData couponData = couponFeignService.findUserCouponRecordById(couponRecordId);
        if (couponData.getCode() == 0) {
            CouponRecordVO couponRecordVO = couponData.getData(new TypeReference<CouponRecordVO>() {
            });
            if (!couponAvailable(couponRecordVO)) {
                log.error("优惠券使用失败");
                throw new BizException(BizCodeEnum.COUPON_UNAVAILABLE);
            }
            return couponRecordVO;
        } else {
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
        }
    }

    /**
     * 判断优惠券是否可用
     *
     * @param couponRecordVO
     * @return
     */
    private boolean couponAvailable(CouponRecordVO couponRecordVO) {
        if (couponRecordVO.getUseState().equalsIgnoreCase(CouponStateEnum.NEW.name())) {
            long timestamp = CommonUtil.getCurrentTimestamp();
            long end = couponRecordVO.getEndTime().getTime();
            long start = couponRecordVO.getStartTime().getTime();
            if (timestamp >= start && timestamp <= end) {
                return true;
            }
        }
        return false;
    }

    private ProductOrderAddressVO getUserAddress(long addressId) {
        JsonData detail = userFeignService.detail(addressId);
        if (detail.getCode() != 0) {
            log.error("获取收货地址失败,msg:{}", detail);
            throw new BizException(BizCodeEnum.ADDRESS_NO_EXITS);
        }
        ProductOrderAddressVO productOrderAddressVO
                = detail.getData(new TypeReference<ProductOrderAddressVO>() {
        });
        log.info("收货地址信息:{}", productOrderAddressVO);
        return productOrderAddressVO;
    }

    @Override
    public String queryProductOrderState(String outTradeNo) {
        ProductOrderDO out_trade_no = baseMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no", outTradeNo));
        if (out_trade_no == null) {
            return "";
        } else {
            return out_trade_no.getState();
        }
    }

    @Override
    public boolean closeProductOrder(OrderMessage orderMessage) {
        ProductOrderDO productOrderDO = baseMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no", orderMessage.getOutTradeNo()));
        if (productOrderDO == null) {
            log.warn("直接确认消息,订单不存在:{}", orderMessage);
            return true;
        }
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {
            log.info("直接确认消息,订单已经支付:{}", orderMessage);
            return true;
        }
        //向第三方支付查询订单是否 未支付
        PayInfoVO payInfoVO = new PayInfoVO();
        payInfoVO.setPayType(productOrderDO.getPayType());
        payInfoVO.setOutTradeNo(orderMessage.getOutTradeNo());
        String payResult = payFactory.queryPaySuccess(payInfoVO);

        if (StringUtils.isBlank(payResult)) {
            baseMapper.updateOrderPayState(productOrderDO.getOutTradeNo(), ProductOrderStateEnum.CANCEL.name(), ProductOrderStateEnum.NEW.name());
            log.info("结果为空,未支付成功,本地取消订单:{}", orderMessage);
            return true;
        } else {
            log.warn("支付成功,支付回调问题:{}", orderMessage);
            baseMapper.updateOrderPayState(productOrderDO.getOutTradeNo(), ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
            return true;
        }
    }

    /**
     * 支付通知接口更新订单状态
     *
     * @param payTypeEnum
     * @param paramsToMap
     * @return
     */
    @Override
    public JsonData handlerOrderCallbackMsg(ProductOrderPayTypeEnum payTypeEnum, Map<String, String> paramsToMap) {
        if (payTypeEnum.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) {
            //获取商户订单号
            String out_trade_no = paramsToMap.get("out_trade_no");
            String trade_status = paramsToMap.get("trade_status");
            if ("TRADE_SUCCESS".equalsIgnoreCase(trade_status) || "TRADE_FINISHED".equalsIgnoreCase(trade_status)) {
                //更新订单状态
                baseMapper.updateOrderPayState(out_trade_no, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
                return JsonData.buildSuccess();
            }
        } else if (payTypeEnum.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) {
//微信支付 TODO
        }
        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
    }

    @Override
    public Map<String, Object> page(int page, int size, String state) {
        LoginUser loginUser = LoginInterceptor.loginUserThreadLocal.get();
        Page<ProductOrderDO> productOrderDOS = new Page<ProductOrderDO>(page, size);
        QueryWrapper<ProductOrderDO> productOrderDOQueryWrapper = new QueryWrapper<>();
        productOrderDOQueryWrapper.eq("user_id", loginUser.getId());
        if (!StringUtils.isBlank(state)) {
            productOrderDOQueryWrapper.eq("state", state);
        }
        Page<ProductOrderDO> productOrderDOPage = baseMapper.selectPage(productOrderDOS, productOrderDOQueryWrapper);
        List<ProductOrderDO> records = productOrderDOPage.getRecords();
        List<ProductOrderVO> productOrderVOList = records.stream().map(t -> {
            ProductOrderVO productOrderVO = new ProductOrderVO();
            BeanUtils.copyProperties(t, productOrderVO);
            List<ProductOrderItemDO> productOrderItemDOList = productOrderItemService.list(new QueryWrapper<ProductOrderItemDO>()
                    .eq("product_order_id", t.getId()));
            List<OrderItemVO> collect = productOrderItemDOList.stream().map(p -> {
                OrderItemVO orderItemVO = new OrderItemVO();
                BeanUtils.copyProperties(p, orderItemVO);
                return orderItemVO;
            }).collect(Collectors.toList());
            productOrderVO.setOrderItemVOList(collect);
            return productOrderVO;
        }).collect(Collectors.toList());

        HashMap<String, Object> pageMap = new HashMap<>(3);
        pageMap.put("total_record", productOrderDOPage.getTotal());
        pageMap.put("total_page", productOrderDOPage.getPages());
        pageMap.put("current_data", productOrderVOList);

        return pageMap;
    }

    @Override
    @Transactional
    public JsonData repay(RepayOrderRequest repayOrderRequest) {
        //TODO 有可能切换支付方式 更新支付方式
        LoginUser loginUser = LoginInterceptor.loginUserThreadLocal.get();
        ProductOrderDO productOrderDO = baseMapper.selectOne(new QueryWrapper<ProductOrderDO>()
                .eq("out_trade_no", repayOrderRequest.getOutTradeNo()));
        log.info("订单状态:{}", productOrderDO);
        if (productOrderDO == null) {
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_NOT_EXIST);
        }
        if (!productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) {
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_STATE_ERROR);
        } else {
            long orderLiveTime = CommonUtil.getCurrentTimestamp() - productOrderDO.getCreateTime().getTime();
            //大于订单超时时间 则失败
            if (orderLiveTime > TimeConstant.ORDER_PAY_TIMEOUT_MILLS) {
                return JsonData.buildResult(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT);
            } else {
                long timeout = TimeConstant.ORDER_PAY_TIMEOUT_MILLS - orderLiveTime;
                //创建支付
                PayInfoVO payInfoVO = new PayInfoVO(productOrderDO.getOutTradeNo(), productOrderDO.getPayAmount(), repayOrderRequest.getPayType(), repayOrderRequest.getClientType()
                        , productOrderDO.getOutTradeNo(), "", timeout);
                log.info("payInfoVO={}0", payInfoVO);
                String payResult = payFactory.pay(payInfoVO);
                if (StringUtils.isBlank(payResult)) {
                    return JsonData.buildSuccess(payResult);
                } else {
                    log.error("创建二次支付订单失败:payinfoVO={},payResult={}", payInfoVO, payResult);
                    return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
                }
            }
        }
    }
}
